package org.simple.redis.impl;

import org.simple.redis.RedisLock;
import redis.clients.jedis.Jedis;

import java.util.Collections;
import java.util.UUID;

/**
 * Redis分布式锁实现
 * 设计思路：采用redis set key if not exist功能实现加锁操作，初始化一个锁，
 * 加锁的时候redis会创建一个key=lockName的item。当本任务运行完成之后，进行解锁操作
 * 解锁操作是删除本条key=本机记录lockValue的值
 * 如果获得锁的机器意外宕机，那么势必造成死锁情况，这样的会超时机制就发挥应有的作用，超时后清空锁
 *
 * @author sisyphus
 * @since RedisLock 0.1.0
 */
public class RedisLockImpl implements RedisLock {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private Jedis redis;

    private String lockName;
    private long timeOut;

    private String uuid;

    public RedisLockImpl(String lockName, long timeOut, Jedis redis) {
        this.lockName = lockName;
        this.timeOut = timeOut;
        this.redis = redis;
    }

    @Override
    public void lock() {
        while (true) {
            if (tryLock()) {
                return;
            }
        }
    }

    @Override
    public boolean tryLock() {
        uuid = UUID.randomUUID().toString();
        String result = redis.set(lockName, uuid, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, timeOut);
        return LOCK_SUCCESS.equals(result);
    }


    @Override
    public void unLock() {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        redis.eval(script, Collections.singletonList(lockName), Collections.singletonList(uuid));
    }


    /**
     * 获取当前时间戳，如果无法做到所有的机器时间同步，
     * 可以采用一个机器提供时间服务，这里调用一个时间服务保证所有机器时钟同步
     *
     * @return 时间戳
     */
    private long getNowTime() {
        return System.currentTimeMillis();
    }

}
